#include "ET++.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "ColorMapper.h"

#include "Class.h"
#include "DevBitmap.h"
#include "WindowColorMap.h"
#include "ET_stdio.h"

bool gDither= TRUE;

//---- ColorMapper -------------------------------------------------------------

NewMetaImpl(ColorMapper,RefCounted, (T(id), T(cnt)));

int ColorMapper::cnt= 0;

ColorMapper::ColorMapper()
{
	id= ColorMapper::cnt++;
}

void ColorMapper::Pixel2RGB(u_long, RGB*)
{
	AbstractMethod("Pixel2RGB");
}

bool ColorMapper::RGB2Pixel(RGB*, u_long*)
{
	AbstractMethod("RGB2Pixel");
	return FALSE;
}

bool ColorMapper::ChangeRGB(u_long, RGB*)
{
	AbstractMethod("ChangeRGB");
	return TRUE;
}

bool ColorMapper::IsGrey()
{
	return FALSE;
}

u_long ColorMapper::MaxPixel()
{
	return 0;
}

bool ColorMapper::HasMask()
{
	return FALSE;
}

OStream& ColorMapper::PrintOn(OStream &os)
{
	Object::PrintOn(os);
	return os << id SP;
}

IStream& ColorMapper::ReadFrom(IStream &is)
{
	Object::ReadFrom(is);
	return is >> id;
}

DevBitmap *ColorMapper::MapColors(WindowColorMap*, DevBitmap*, int)
{
	return 0;
}

//---- MonoMapper --------------------------------------------------------------

NewMetaImpl0(MonoMapper,ColorMapper);

MonoMapper::MonoMapper()
{
}

void MonoMapper::Pixel2RGB(u_long pixel, RGB *result)
{
	if (pixel == 0) {
		result->red= result->green= result->blue= 255;
		result->alpha= 0;
	} else if (pixel == 1) {
		result->red= result->green= result->blue= 0;
		result->alpha= 1;
	}
}

bool MonoMapper::RGB2Pixel(RGB *rgb, u_long *pixel)
{
	if (rgb->red == rgb->green && rgb->green == rgb->blue) {
		if (rgb->red == 255) {
			*pixel= 0;
			return TRUE;
		}
		if (rgb->red == 0) {
			*pixel= 1;
			return TRUE;
		}
	}
	return FALSE;
}

bool MonoMapper::ChangeRGB(u_long, RGB*)
{
	return TRUE;
}

bool MonoMapper::IsGrey()
{
	return TRUE;
}

u_long MonoMapper::MaxPixel()
{
	return 1;
}

//---- ColorMap ----------------------------------------------------------------

NewMetaImpl(IndexMapper,ColorMapper, (TV(map,size), T(maxsize), TB(mask)));

IndexMapper::IndexMapper(int mx)
{
	maxsize= mx;
	size= 0;
	map= 0;
	mask= FALSE;
}

IndexMapper::~IndexMapper()
{
	SafeDelete(map);
}

void IndexMapper::Expand(int sz)
{
	if (sz > size) {
		map= (RGB*) Storage::ReAlloc(map, sz*sizeof(RGB));
		size= sz;
	}
}

bool IndexMapper::RGB2Pixel(RGB *rgb, u_long *pixel)
{
	for (u_long i= 0; i < size; i++) {
		if (map[i] == *rgb) {
			*pixel= i;
			return TRUE;
		}
	}
	if (ChangeRGB(i, rgb)) {
		*pixel= i;
		return TRUE;
	}
	return FALSE;
}

bool IndexMapper::ChangeRGB(u_long pixel, RGB *rgb)
{
	if (pixel >= maxsize)
		return FALSE;
	Expand((int)(pixel+1));
	map[pixel]= *rgb;
	if (rgb->alpha != 255)
		mask= TRUE;
	return TRUE;
}

void IndexMapper::Pixel2RGB(u_long pixel, RGB *result)
{
	if (pixel < size)
		*result= map[pixel];
	else
		fprintf(stderr, "IndexMapper::Pixel2RGB: index (%d) out of range\n", pixel);
}

u_long IndexMapper::MaxPixel()
{
	return size-1;
}

bool IndexMapper::HasMask()
{
	return mask;
}

OStream& IndexMapper::PrintOn(OStream &os)
{
	ColorMapper::PrintOn(os);
	os << size SP << maxsize NL;
	if (size > 0) {
		for (int i= 0; i < size; i++)
			os << map[i];
	}
	return os;
}

IStream& IndexMapper::ReadFrom(IStream &is)
{
	int sz;
	ColorMapper::ReadFrom(is);

	mask= FALSE;
	size= 0;

	is >> sz >> maxsize;
	if (sz > 0) {
		Expand(sz);
		for (int i= 0; i < size; i++) {
			is >> map[i];
			if (map[i].alpha != 255)
				mask= TRUE;
		}
	}
	return is;
}

bool IndexMapper::IsGrey()
{
	for (int i= 0; i < size; i++)
		if (! map[i].IsGrey())
			return FALSE;
	return TRUE;
}

DevBitmap *IndexMapper::MapColors(WindowColorMap *wcm, DevBitmap *from, int d)
{
	DevBitmap *to= from->DevAllocBitmap(from->Size(), d);
	if (gDither && ((IsGrey() && MaxPixel() > 16 && !HasMask()) || wcm->IsGrey())) {
		to->DevHalftone(wcm, from, this);
	} else {
		register int x, l= (int) MaxPixel()+1;
		register u_long *map= new u_long[l];
		RGB c;
		
		for (x= 0; x < l; x++) {
			Pixel2RGB(x, &c);
			if (c.alpha == 0)
				map[x]= 0;
			else
				map[x]= wcm->RGB2Index(&c);
		}
		
		to->DevMapColors(from, map);

		delete map;
	}
	return to;
}

//---- DirectMapper ------------------------------------------------------------

NewMetaImpl0(DirectMapper,ColorMapper);

DirectMapper::DirectMapper()
{
}

u_long DirectMapper::MaxPixel()
{
	return 0x00ffffff;
}

bool DirectMapper::IsGrey()
{
	return FALSE;
}

static union {
	struct {
		u_int a : 8;
		u_int b : 8;
		u_int g : 8;
		u_int r : 8;
	} rgb;
	u_long pixel;
} cv;

void DirectMapper::Pixel2RGB(u_long pixel, RGB *result)
{
	/*
	result->alpha= (short) ((pixel & 0xff000000) >> 24);
	result->blue=  (short) ((pixel & 0x00ff0000) >> 16);
	result->green= (short) ((pixel & 0x0000ff00) >> 8);
	result->red=   (short)  (pixel & 0x000000ff);
	*/
	cv.pixel= pixel;
	result->alpha= cv.rgb.a;
	result->blue= cv.rgb.b;
	result->green= cv.rgb.g;
	result->red= cv.rgb.r;
}

bool DirectMapper::RGB2Pixel(RGB *rgb, u_long *pixel)
{
	cv.rgb.a= rgb->alpha;
	cv.rgb.b= rgb->blue;
	cv.rgb.g= rgb->green;
	cv.rgb.r= rgb->red;
	*pixel= cv.pixel;

	// *pixel= ((((rgb->alpha << 8 + rgb->blue) << 8) + rgb->green) << 8) + rgb->red;
	// *pixel= (((rgb->blue << 8) + rgb->green) << 8) + rgb->red;
	return TRUE;
}

DevBitmap *DirectMapper::MapColors(WindowColorMap *wcm, DevBitmap *from, int d)
{
	DevBitmap *to= from->DevAllocBitmap(from->Size(), d);
	if (gDither && !HasMask())
		to->DevFloydSteinberg(wcm, from, this);
	else
		to->DevMapDirect(wcm, from, this);
	return to;
}

